package main

import (
	"bytes"
	"compress/gzip"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	ffi "github.com/filecoin-project/filecoin-ffi"
	"github.com/filecoin-project/specs-storage/storage"
)

const (
	// c2ServiceKey c2服务
	c2ServiceKey      = "c2_service"
	c2AllServiceKey   = "c2_service:all"
	defaultServiceTLL = 60 * 60 * 24 * time.Second
)

type Service struct {
	key      string //缓存key
	Address  string // 连接地址 IP:port
	TaskNum  int    `json:"task_num"`  //正在执行的任务数
	LimitNum int    `json:"limit_num"` //限制任务数
	CurTime  int    `json:"cur_time"`  //当前时间戳
}

// AllServices 获取所有服务
func AllServices() ([]*Service, error) {
	all := []*Service{}
	keys, err := ZRange(c2AllServiceKey)
	if err != nil {
		return nil, err
	}
	for _, k := range keys {
		all = append(all, NewService(k))
	}
	return all, nil
}

// NewService 创建新服务
func NewService(key string) *Service {
	return &Service{key: key}
}

// Address 服务地址
func (s *Service) GetAddress() string {
	if s.Address == "" {
		s.Info()
	}
	return s.Address
}

// Info 服务信息
func (s *Service) Info() *Service {
	jsonstr, err := Get(s.key)
	if err == nil {
		json.Unmarshal([]byte(jsonstr), s)
	}
	return s
}

// // GpuInfo 获取gpu 信息
// func (s *Service) GpuInfo() []mtl.Device {
// 	return mtl.CopyAllDevices()
// }

// LimitNum 任务限制数
func (s *Service) GetLimitNum() int {
	// gpus := s.GpuInfo()
	// fmt.Printf("gpus %+v", gpus)
	// 暂时只支持一个gpu
	count := 1
	return count
}

// SaveInfo 保存信息
func (s *Service) SaveInfo() error {
	jsonstr, err := json.Marshal(&s)
	if err != nil {
		return err
	}
	return Set(s.key, string(jsonstr), defaultServiceTLL)
}

// AddTaskNum 新增正在执行的任务
func (s *Service) AddTaskNum(num int) bool {
	if !s.Lock() {
		return false
	}
	defer s.Unlock()
	s.TaskNum = s.Info().TaskNum + num
	return s.SaveInfo() == nil
}

// Lock 加锁
func (s *Service) Lock() bool {
	key := s.lockKey()
	ok, err := SetNX(key, s.Address, defaultServiceTLL)
	if !ok || err != nil {
		return false
	}
	return true
}

// cacheKey 缓存key
func (s *Service) cacheKey(name string) string {
	return CacheKey(name + ":" + s.Address)
}

// DoWork 执行任务
func (s *Service) DoWork(request *Commit2Request) (storage.Proof, error) {
	return ffi.SealCommitPhase2(request.Commit1Out, request.SectorID.Number, request.SectorID.Miner)
}

// C2 执行任务
func (s *Service) C2(request *Commit2Request) (storage.Proof, error) {
	b, err := json.Marshal(request)
	if err != nil {
		log.Errorf("json format error: %+v", err)
		return nil, err
	}

	zBuf := new(bytes.Buffer)
	zw := gzip.NewWriter(zBuf)
	if _, err = zw.Write(b); err != nil {
		log.Errorf("commit2 gzip failed, error: %+v", err)
	}

	zw.Flush()
	zw.Close()
	req, err := http.NewRequest("POST", fmt.Sprintf("http://%s/proxy/commit2", s.Address), zBuf)
	if err != nil {
		log.Errorf("http new request err: %+v", err)
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json;charset=utf-8")
	req.Header.Set("Content-Encoding", "gzip")

	log.Info("Send commit2 task to extern server and wait for response ......")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Error("do request err: ", err)
		return nil, err
	}

	if resp.StatusCode != 200 {
		log.Errorf("http client do code: %d err: %+v", resp.StatusCode, err)
		return nil, err
	}
	defer resp.Body.Close()

	result, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("ioutil read all err: %+v", err)
		return nil, err
	}
	response := Commit2Response{}
	err = json.Unmarshal(result, &response)
	if err != nil {
		log.Errorf("json format len: %d error: %+v", len(result), err)
		return nil, err
	}

	log.Info("Receive commit2 result success......")
	return response.Proof, nil
}

// Register 注册服务
func (s *Service) Register(address string) (func(context.Context) error, error) {
	s.Address = address
	s.LimitNum = s.GetLimitNum()
	err := s.SaveInfo()
	if err != nil {
		return s.Close, err
	}
	// 写入有序集合
	//todo 可以根据c2 执行时间排序
	err = ZAdd(c2AllServiceKey, s.cacheKey("info"), 0)

	return s.Close, err
}

// Close 关闭服务
func (s *Service) Close(context.Context) error {

	Del(s.cacheKey("info"))

	ZRem(c2AllServiceKey, s.cacheKey("info"))
	return nil
}

// lockKey 锁key
func (s *Service) lockKey() string {
	return s.cacheKey("lock")
}

// Unlock 解锁
func (s *Service) Unlock() bool {
	num, err := Del(s.lockKey())
	if err != nil || num == 0 {
		return false
	}
	return true
}

// HasAbleTaskNum 还能执行多少任务
func (s *Service) HasAbleTaskNum() int {
	info := s.Info()
	return s.LimitNum - info.TaskNum
}

func CacheKey(key string) string {
	return fmt.Sprintf("%s:%s:%s", c2ServiceKey, c2Version, key)
}
